package com.choosefine.paycenter.common.utils;

import com.choosefine.paycenter.common.component.RedisLock;
import com.choosefine.paycenter.common.constants.SNFlagConstants;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.support.atomic.RedisAtomicLong;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;

/**
 * 流水号生成工具
 * Created by Dengyouyi on 2017/4/7.
 */
@Slf4j
@Component
public class SerialNumberUtils {

    @Autowired
    private RedisTemplate redisTemplate;

    private static final String DEFAULT_PATTERN = "yyyyMMddHHmmss";

    private static final String BIZZ_SN_PATTERN = "yyyyMMdd";

    private static final String BANK_REQUEST_DEFAULT_PATTERN = "yyyyMMdd";

    private static final String CHARS = "0123456789";
    /**
     * 当等于此数时重置为初始值
     */
    private static final long OPERATION_CODE = 100000000L;
    /**
     * 支付流水号自增
     */
    private static final String UNIQUE_COUNTER_PAY_SN_NAME = "UNIQUE_COUNTER_PAY_SN";

    /**
     * 充值流水号自增
     */
    private static final String UNIQUE_COUNTER_R_SN_NAME = "UNIQUE_COUNTER_R_SN";
    /**
     * 提现流水号自增
     */
    private static final String UNIQUE_COUNTER_W_SN_NAME = "UNIQUE_COUNTER_W_SN";

    /**
     * 生成普通转账流水号(相当于业务流水号,只不过这个是支付系统的业务流水号，等同于劳务、商城的业务流水号)
     */
    private static final String UNIQUE_COUNTER_NORMAL_TRANSFER_SN_NAME = "UNIQUE_COUNTER_NORMAL_TRANSFER_SN";

    /**
     * 生成账单发起的流水号（相当于业务流水号，从账单发起的支付，实际作用是给予选择支付渠道、支付方式时，使用）
     */
    private static final String UNIQUE_COUNTER_BILL_BIZZ_SN_NAME = "UNIQUE_COUNTER_BILL_BIZZ_SN";

    /**
     * 银行转账请求查询流水号
     */
    private static final String UNIQUE_COUNTER_BANK_REQUEST_SN_NAME = "UNIQUE_COUNTER_BANK_REQUEST_SN";

    private RedisLock paySnLock;
    private RedisLock wSnLock;
    private RedisLock rSnLock;
    private RedisLock bankRSnLock;

    /**业务系统流水号（这里业务系统指支付系统自身）*/
    private RedisLock transferSnLock;

    /**从账单发起需要生成*/
    private RedisLock payFromBillbizzSnLock;

    @PostConstruct
    public void init(){
        paySnLock = new RedisLock(redisTemplate, UNIQUE_COUNTER_PAY_SN_NAME);
        rSnLock = new RedisLock(redisTemplate, UNIQUE_COUNTER_R_SN_NAME);
        wSnLock = new RedisLock(redisTemplate, UNIQUE_COUNTER_W_SN_NAME);
        bankRSnLock = new RedisLock(redisTemplate, UNIQUE_COUNTER_BANK_REQUEST_SN_NAME);
        transferSnLock = new RedisLock(redisTemplate,UNIQUE_COUNTER_NORMAL_TRANSFER_SN_NAME);
        payFromBillbizzSnLock = new RedisLock(redisTemplate,UNIQUE_COUNTER_BILL_BIZZ_SN_NAME);
    }

    /**
     * 获取交易流水号,标识位1
     *
     * @return String
     */
    public String getPaySn() {
        try {
            paySnLock.lock();
            StringBuilder number = new StringBuilder();
            number.append(getNow())
                    .append(SNFlagConstants.PAY_SN_FLAG)
                    .append(RandomStringUtils.random(3,CHARS))
                    .append(getRedisIncrease(UNIQUE_COUNTER_PAY_SN_NAME));
            return number.toString();
        } catch (Exception e) {
            log.error("获取支付单的自增序号(8位)redis锁失败", e);
            throw new RuntimeException(e);
        } finally {
            paySnLock.unlock();
        }
    }

    /**
     * 获取充值流水号,标识位2
     *
     * @return String
     */
    public String getRechargeSn() {
        try {
            rSnLock.lock();
            StringBuilder number = new StringBuilder();
            number.append(getNow())
                    .append(SNFlagConstants.R_SN_FLAG)
                    .append(RandomStringUtils.random(3,CHARS))
                    .append(getRedisIncrease(UNIQUE_COUNTER_R_SN_NAME));
            return number.toString();
        } catch (Exception e) {
            log.error("获取充值单的自增序号(8位)redis锁失败", e);
            throw new RuntimeException(e);
        } finally {
            rSnLock.unlock();
        }
    }

    /**
     *
     * 获取银行转账查询流水号
     *
     * @return String
     */
    public String getBankRequestSn(){
        try {
            bankRSnLock.lock();
            StringBuilder number = new StringBuilder();
            number.append(DateFormatUtils.format(System.currentTimeMillis(), BANK_REQUEST_DEFAULT_PATTERN))
                    .append(getRedisIncrease(UNIQUE_COUNTER_BANK_REQUEST_SN_NAME));
            return number.toString();
        } catch (Exception e) {
            log.error("获取银行转账查询流水号自增序号(8位)redis锁失败", e);
            throw new RuntimeException(e);
        } finally {
            bankRSnLock.unlock();
        }
    }

    /**
     * 获取提现流水号,标识位3
     *
     * @return String
     */
    public String getWithdrawSn(){
        try {
            wSnLock.lock();
            StringBuilder number = new StringBuilder();
            number.append(getNow())
                    .append(SNFlagConstants.W_SN_FLAG)
                    .append(RandomStringUtils.random(3,CHARS))
                    .append(getRedisIncrease(UNIQUE_COUNTER_W_SN_NAME));
            return number.toString();
        } catch (Exception e) {
            log.error("获取提现单的自增序号(8位)redis锁失败", e);
            throw new RuntimeException(e);
        } finally {
            wSnLock.unlock();
        }
    }

    /**
     * 获取从账单发起支付的业务流水号,标识位4
     *
     * @return String
     */
    public String getPayBizzSn(){
        try {
            payFromBillbizzSnLock.lock();
            StringBuilder number = new StringBuilder();
            number.append(getNow(BIZZ_SN_PATTERN))
                    .append(SNFlagConstants.P_BIZZ_SN_FLAG)
                    .append(RandomStringUtils.random(3,CHARS))
                    .append(getRedisIncrease(UNIQUE_COUNTER_BILL_BIZZ_SN_NAME));
            return number.toString();
        } catch (Exception e) {
            log.error("获取从账单发起支付的业务流水号(8位)redis锁失败", e);
            throw new RuntimeException(e);
        } finally {
            payFromBillbizzSnLock.unlock();
        }
    }
    /**
     * 获取普通转账流水号（会传给创建支付单接口的，作为业务系统流水号）
     *
     */
    public String getSelfNormalTransferSn(){
        try {
            transferSnLock.lock();
            StringBuilder number = new StringBuilder();
            number.append(getNow(BIZZ_SN_PATTERN))
                    .append(SNFlagConstants.TRANSFER_BIZZ_SN_FLAG)
                    .append(RandomStringUtils.random(3,CHARS))
                    .append(getRedisIncrease(UNIQUE_COUNTER_NORMAL_TRANSFER_SN_NAME));
            return number.toString();
        } catch (Exception e) {
            log.error("获取普通转账流水号的自增序号(8位)redis锁失败", e);
            throw new RuntimeException(e);
        } finally {
            transferSnLock.unlock();
        }
    }


    /**
     * 获取redis8位递增值
     *
     * @return String
     */
    private synchronized String getRedisIncrease(String counterName) {
        RedisAtomicLong counter = new RedisAtomicLong(counterName, redisTemplate.getConnectionFactory());
        long value = counter.get();
        counter.set(value = (++value >= OPERATION_CODE ? 0 :value));
        return String.format("%08d",value);
    }

    public static String getNow() {
        return getNow(DEFAULT_PATTERN);
    }


    public static String getNow(String pattern) {
        long currentTimeMillis = System.currentTimeMillis();
        return DateFormatUtils.format(currentTimeMillis,pattern);
    }


    /**
     * 是否为支付流水号
     * @param sn
     * @return
     */
    public boolean isPaySn(String sn){
        if(sn.length() <= 14) {return false;}
        return SNFlagConstants.PAY_SN_FLAG.equals(sn.substring(14,15));
    }

    /**
     * 是否为充值流水号
     * @param sn
     * @return
     */
    public boolean isRechargeSn(String sn){
        if(sn.length() <= 14) {return false;}
        return SNFlagConstants.R_SN_FLAG.equals(sn.substring(14,15));
    }

    /**
     * 是否为提现流水号
     * @param sn
     * @return
     */
    public boolean isWithdrawSn(String sn){
        if(sn.length() <= 14) {return false;}
        return SNFlagConstants.W_SN_FLAG.equals(sn.substring(14,15));
    }

    /**
     * 是否为从账单支付的业务流水号
     * @param sn
     * @return
     */
    public boolean isPayBizzSn(String sn){
        if(sn.length() <= 8) {return false;}
        return SNFlagConstants.P_BIZZ_SN_FLAG.equals(sn.substring(8,9));
    }

    /**
     * 是否为转账的业务流水号
     * @param sn
     * @return
     */
    public boolean isTransferBizzSn(String sn){
        if(sn.length() <= 8) {return false;}
        return SNFlagConstants.TRANSFER_BIZZ_SN_FLAG.equals(sn.substring(8,9));
    }
}